Õppige, kuidas rakendada edenemise hindamist ja lõpetamisaja ennustamist, kasutades Reacti useFormStatus hooki, parandades kasutajakogemust andmemahukates rakendustes.
Reacti useFormStatus edenemise hindamine: lõpetamisaja ennustamine
Reacti useFormStatus hook, mis tutvustati React 18-s, pakub väärtuslikku teavet vormi esitamise oleku kohta. Kuigi see ei paku otse edenemise hindamist, saame selle omadusi ja teisi tehnikaid ära kasutada, et pakkuda kasutajatele sisukat tagasisidet potentsiaalselt pikaajaliste vormiesituste ajal. See postitus uurib meetodeid edenemise hindamiseks ja lõpetamisaja ennustamiseks useFormStatus'i kasutamisel, mis toob kaasa kaasahaaravama ja kasutajasõbralikuma kogemuse.
useFormStatus'i mõistmine
Enne edenemise hindamisse süvenemist vaatame kiirelt üle useFormStatus'i eesmärgi. See hook on mõeldud kasutamiseks <form> elemendi sees, mis kasutab action'i atribuuti. See tagastab objekti, mis sisaldab järgmisi omadusi:
pending: Tõeväärtus, mis näitab, kas vormi hetkel esitatakse.data: Andmed, mis vormiga esitati (kui esitamine oli edukas).method: Vormi esitamiseks kasutatud HTTP-meetod (nt 'POST', 'GET').action: Funktsioon, mis anti vormiaction'i atribuudile.error: Veaobjekt, kui esitamine ebaõnnestus.
Kuigi useFormStatus annab meile teada, kas vormi esitatakse, ei anna see otsest teavet esitamise edenemise kohta, eriti kui action'i funktsioon hõlmab keerulisi või aeganõudvaid operatsioone.
Edenemise hindamise väljakutse
Peamine väljakutse seisneb selles, et action'i funktsiooni täitmine on Reacti jaoks läbipaistmatu. Me ei tea olemuslikult, kui kaugel protsess on. See kehtib eriti serveripoolsete operatsioonide puhul. Siiski saame selle piirangu ületamiseks kasutada erinevaid strateegiaid.
Strateegiad edenemise hindamiseks
Siin on mitu lähenemist, mida saate kasutada, igaühel neist on oma plussid ja miinused:
1. Server-Sent Events (SSE) või WebSockets
Kõige robustsem lahendus on sageli saata edenemise uuendusi serverist kliendile. Seda saab saavutada kasutades:
- Server-Sent Events (SSE): Ühesuunaline (serverist-kliendile) protokoll, mis võimaldab serveril saata uuendusi kliendile üle ühe HTTP-ühenduse. SSE on ideaalne, kui klient peab ainult uuendusi *vastu võtma*.
- WebSockets: Kahesuunaline suhtlusprotokoll, mis pakub püsivat ühendust kliendi ja serveri vahel. WebSockets sobib reaalajas uuenduste jaoks mõlemas suunas.
Näide (SSE):
Serveripoolne (Node.js):
const express = require('express');
const app = express();
app.get('/progress', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
let progress = 0;
const interval = setInterval(() => {
progress += 10;
if (progress > 100) {
progress = 100;
clearInterval(interval);
res.write(`data: {"progress": ${progress}, "completed": true}\n\n`);
res.end();
} else {
res.write(`data: {"progress": ${progress}, "completed": false}\n\n`);
}
}, 500); // Simulate progress update every 500ms
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
Kliendipoolne (React):
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [progress, setProgress] = useState(0);
useEffect(() => {
const eventSource = new EventSource('/progress');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
setProgress(data.progress);
if (data.completed) {
eventSource.close();
}
};
eventSource.onerror = (error) => {
console.error('EventSource failed:', error);
eventSource.close();
};
return () => {
eventSource.close();
};
}, []);
return (
<div>
<p>Progress: {progress}%</p>
</div>
);
}
export default MyComponent;
Selgitus:
- Server seab SSE jaoks sobivad päised.
- Server saadab edenemise uuendusi
data:sĂĽndmustena. Iga sĂĽndmus on JSON-objekt, mis sisaldabprogress'i jacompletedlippu. - Reacti komponent kasutab
EventSource'i nende sĂĽndmuste kuulamiseks. - Komponent uuendab olekut (
progress) vastuvõetud sündmuste põhjal.
Eelised: Täpsed edenemise uuendused, reaalajas tagasiside.
Puudused: Nõuab serveripoolseid muudatusi, keerukam implementatsioon.
2. KĂĽsitlemine API otspunkti kaudu
Kui te ei saa kasutada SSE-d või WebSockets'e, saate rakendada küsitlemist (polling). Klient saadab perioodiliselt päringuid serverile, et kontrollida operatsiooni olekut.
Näide:
Serveripoolne (Node.js):
const express = require('express');
const app = express();
// Simulate a long-running task
let taskProgress = 0;
let taskId = null;
app.post('/start-task', (req, res) => {
taskProgress = 0;
taskId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); // Generate a unique task ID
// Simulate background processing
const interval = setInterval(() => {
taskProgress += 10;
if (taskProgress >= 100) {
taskProgress = 100;
clearInterval(interval);
}
}, 500);
res.json({ taskId });
});
app.get('/task-status/:taskId', (req, res) => {
if (req.params.taskId === taskId) {
res.json({ progress: taskProgress });
} else {
res.status(404).json({ message: 'Task not found' });
}
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
Kliendipoolne (React):
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [progress, setProgress] = useState(0);
const [taskId, setTaskId] = useState(null);
const startTask = async () => {
const response = await fetch('/start-task', { method: 'POST' });
const data = await response.json();
setTaskId(data.taskId);
};
useEffect(() => {
if (!taskId) return;
const interval = setInterval(async () => {
const response = await fetch(`/task-status/${taskId}`);
const data = await response.json();
setProgress(data.progress);
if (data.progress === 100) {
clearInterval(interval);
}
}, 1000); // Poll every 1 second
return () => clearInterval(interval);
}, [taskId]);
return (
<div>
<button onClick={startTask} disabled={taskId !== null}>Start Task</button>
{taskId && <p>Progress: {progress}%</p>}
</div>
);
}
export default MyComponent;
Selgitus:
- Klient alustab ülesannet, kutsudes välja
/start-task, ja saab vastutaskId. - Seejärel küsitleb klient perioodiliselt
/task-status/:taskId, et saada edenemise infot.
Eelised: Suhteliselt lihtne implementeerida, ei nõua püsivaid ühendusi.
Puudused: Võib olla vähem täpne kui SSE/WebSockets, tekitab latentsust küsitlemise intervalli tõttu, koormab serverit sagedaste päringutega.
3. Optimistlikud uuendused ja heuristika
Mõnel juhul saate kasutada optimistlikke uuendusi koos heuristikaga, et pakkuda mõistlikku hinnangut. Näiteks kui laadite faile üles, saate kliendipoolselt jälgida üleslaaditud baitide arvu ja hinnata edenemist faili kogusuuruse põhjal.
Näide (faili üleslaadimine):
import React, { useState } from 'react';
function MyComponent() {
const [progress, setProgress] = useState(0);
const [file, setFile] = useState(null);
const handleFileChange = (event) => {
setFile(event.target.files[0]);
};
const handleSubmit = async (event) => {
event.preventDefault();
if (!file) return;
const formData = new FormData();
formData.append('file', file);
try {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percentage = Math.round((event.loaded * 100) / event.total);
setProgress(percentage);
}
});
xhr.open('POST', '/upload'); // Replace with your upload endpoint
xhr.send(formData);
xhr.onload = () => {
if (xhr.status === 200) {
console.log('Upload complete!');
} else {
console.error('Upload failed:', xhr.status);
}
};
xhr.onerror = () => {
console.error('Upload failed');
};
} catch (error) {
console.error('Upload error:', error);
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<input type="file" onChange={handleFileChange} />
<button type="submit" disabled={!file}>Upload</button>
</form>
<p>Progress: {progress}%</p>
</div>
);
}
export default MyComponent;
Selgitus:
- Komponent kasutab faili ĂĽleslaadimiseks
XMLHttpRequestobjekti. - Üleslaadimise edenemise jälgimiseks kasutatakse
progresssĂĽndmuse kuulajatxhr.uploadpeal. - Protsendi arvutamiseks kasutatakse sĂĽndmuse
loadedjatotalomadusi.
Eelised: Ainult kliendipoolne, võib anda kohest tagasisidet.
Puudused: Täpsus sõltub heuristika usaldusväärsusest, ei pruugi sobida igat tüüpi operatsioonide jaoks.
4. Tegevuse jagamine väiksemateks sammudeks
Kui action funktsioon teostab mitu eraldiseisvat sammu, saate pärast iga sammu kasutajaliidest uuendada, et näidata edenemist. See nõuab action funktsiooni muutmist, et see pakuks uuendusi.
Näide:
import React, { useState } from 'react';
async function myAction(setProgress) {
setProgress(10);
await someAsyncOperation1();
setProgress(40);
await someAsyncOperation2();
setProgress(70);
await someAsyncOperation3();
setProgress(100);
}
function MyComponent() {
const [progress, setProgress] = useState(0);
const handleSubmit = async () => {
await myAction(setProgress);
};
return (
<div>
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
<p>Progress: {progress}%</p>
</div>
);
}
export default MyComponent;
Selgitus:
- Funktsioon
myActionaktsepteeribsetProgresstagasikutset. - See uuendab edenemise olekut oma täitmise erinevates punktides.
Eelised: Otsene kontroll edenemise uuenduste ĂĽle.
Puudused: Nõuab action funktsiooni muutmist, võib olla keerulisem implementeerida, kui samme ei ole lihtne jaotada.
Lõpetamisaja ennustamine
Kui teil on edenemise uuendused, saate neid kasutada hinnangulise järelejäänud aja ennustamiseks. Lihtne lähenemine on jälgida teatud edenemistaseme saavutamiseks kulunud aega ja ekstrapoleerida see koguaja hindamiseks.
Näide (lihtsustatud):
import React, { useState, useEffect, useRef } from 'react';
function MyComponent() {
const [progress, setProgress] = useState(0);
const [estimatedTimeRemaining, setEstimatedTimeRemaining] = useState(null);
const startTimeRef = useRef(null);
useEffect(() => {
if (progress > 0 && startTimeRef.current === null) {
startTimeRef.current = Date.now();
}
if (progress > 0) {
const elapsedTime = Date.now() - startTimeRef.current;
const estimatedTotalTime = (elapsedTime / progress) * 100;
const remainingTime = estimatedTotalTime - elapsedTime;
setEstimatedTimeRemaining(Math.max(0, remainingTime)); // Ensure non-negative
}
}, [progress]);
// ... (rest of the component and progress updates as described in previous sections)
return (
<div>
<p>Progress: {progress}%</p>
{estimatedTimeRemaining !== null && (
<p>Estimated Time Remaining: {Math.round(estimatedTimeRemaining / 1000)} seconds</p>
)}
</div>
);
}
export default MyComponent;
Selgitus:
- Salvestame algusaja, kui edenemist esmakordselt uuendatakse.
- Arvutame kulunud aja ja kasutame seda koguaja hindamiseks.
- Arvutame järelejäänud aja, lahutades kulunud aja hinnangulisest koguajast.
Olulised kaalutlused:
- Täpsus: See on *väga* lihtsustatud ennustus. Võrgutingimused, serveri koormus ja muud tegurid võivad täpsust oluliselt mõjutada. Keerukamad tehnikad, nagu keskmistamine mitme intervalli jooksul, võivad täpsust parandada.
- Visuaalne tagasiside: Märkige selgelt, et aeg on *hinnanguline*. Vahemike kuvamine (nt „Hinnanguline järelejäänud aeg: 5-10 sekundit”) võib olla realistlikum.
- Äärmusjuhud: Käsitlege äärmusjuhte, kus edenemine on alguses väga aeglane. Vältige nulliga jagamist või ülemäära suurte hinnangute kuvamist.
useFormStatus'i kombineerimine edenemise hindamisega
Kuigi useFormStatus ise ei paku edenemise teavet, saate selle pending omadust kasutada edenemisindikaatori sisse- või väljalülitamiseks. Näiteks:
import React, { useState } from 'react';
import { useFormStatus } from 'react-dom';
// ... (Progress estimation logic from previous examples)
function MyComponent() {
const [progress, setProgress] = useState(0);
const { pending } = useFormStatus();
const handleSubmit = async (formData) => {
// ... (Your form submission logic, including updates to progress)
};
return (
<form action={handleSubmit}>
<button type="submit" disabled={pending}>Submit</button>
{pending && <p>Progress: {progress}%</p>}
</form>
);
}
Selles näites kuvatakse edenemisindikaatorit ainult siis, kui vorm on ootel (st kui useFormStatus.pending on true).
Parimad praktikad ja kaalutlused
- Eelistage täpsust: Valige edenemise hindamise tehnika, mis sobib teostatava operatsiooni tüübiga. SSE/WebSockets annavad üldiselt kõige täpsemaid tulemusi, samas kui heuristika võib olla piisav lihtsamate ülesannete jaoks.
- Pakkuge selget visuaalset tagasisidet: Kasutage edenemisribasid, keerlejaid või muid visuaalseid vihjeid, et näidata, et operatsioon on pooleli. Märgistage selgelt edenemisindikaator ja vajadusel hinnanguline järelejäänud aeg.
- Käsitlege vigu sujuvalt: Kui operatsiooni ajal tekib viga, kuvage kasutajale informatiivne veateade. Vältige edenemisindikaatori kinnijäämist teatud protsendile.
- Optimeerige jõudlust: Vältige arvutusmahukate operatsioonide teostamist kasutajaliidese lõimes, kuna see võib jõudlust negatiivselt mõjutada. Kasutage töö taustalõimedesse suunamiseks veebitöölisi (web workers) või muid tehnikaid.
- Juurdepääsetavus: Veenduge, et edenemisindikaatorid on juurdepääsetavad puuetega kasutajatele. Kasutage ARIA atribuute, et pakkuda semantilist teavet operatsiooni edenemise kohta. Näiteks kasutage edenemisribal
aria-valuenow,aria-valueminjaaria-valuemax. - Lokaliseerimine: Hinnangulise järelejäänud aja kuvamisel arvestage erinevate ajavormingute ja piirkondlike eelistustega. Kasutage aja sobivaks vormindamiseks vastavalt kasutaja lokaadile teeke nagu
date-fnsvõimoment.js. - Rahvusvahelistamine: Veateated ja muu tekst tuleks rahvusvahelistada, et toetada mitut keelt. Tõlgete haldamiseks kasutage teeki nagu
i18next.
Kokkuvõte
Kuigi Reacti useFormStatus hook ei paku otseselt edenemise hindamise võimalusi, saate seda kombineerida teiste tehnikatega, et pakkuda kasutajatele sisukat tagasisidet vormide esitamise ajal. Kasutades SSE/WebSockets'e, küsitlemist, optimistlikke uuendusi või tegevuste jaotamist väiksemateks sammudeks, saate luua kaasahaaravama ja kasutajasõbralikuma kogemuse. Pidage meeles, et peate eelistama täpsust, pakkuma selget visuaalset tagasisidet, käsitlema vigu sujuvalt ja optimeerima jõudlust, et tagada positiivne kogemus kõikidele kasutajatele, sõltumata nende asukohast või taustast.